﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Mogre;

using System.Drawing;
using System.Runtime.InteropServices;

namespace ProjetSMA.View.Mogre
{
    public class Entity3D 
    {
       
        protected AnimationState _animState;
        protected AnimationState _animState_bot = null;
        protected float _animationSpeed;
        protected Model.ItemMove m_item;
        protected Root m_root;
        protected SceneManager m_scnmgr;
        protected string m_name = " ";
        protected Vector3 m_direction = Vector3.ZERO;
        protected Vector3 m_destination = Vector3.ZERO;

        protected float m_distance = 0.0f;
        protected float m_walkSpeed = 12;
        protected SceneNode m_node = null;
        protected int m_length_item;
        protected bool m_walking = false;

        protected float m_angle = 0;
        protected float m_angle_next = 0;
        protected float m_angle_orientation = 0;

        protected List<Element3D> m_copy_world = new List<Element3D>();
        public Map3D m_world;
        protected List<Element3D> m_elements_3D_viewed = new List<Element3D>();
        protected List<Element3D> m_elements_a_star = new List<Element3D>();
        protected List<Element3D> m_elements_perception = new List<Element3D>();


        protected float m_decalage = 0;
        protected float m_decalage_speed = 1;
        protected int m_cpt = 0;
        protected int m_cpt_2 = 0;

        protected Element3D m_position_3D;
        protected float m_speed = 0;

        protected List<RayLine> m_rays = new List<RayLine>();
        protected Pattern3D m_pattern;
        protected BillboardSet m_billboard_set;
        protected Billboard m_billboard;

        protected double m_angle_radian = 0;
        protected Vector3 m_tmp_v3 = new Vector3(0, 0, 0);

        protected float m_dec = 0;
        protected float m_delta = 0;

        protected Breed3D m_breed;
        protected Die3D m_die;

        protected float m_scale = 1f;
        protected float m_cpt_scale = 0.001F;
        protected float m_scale_max = 2;
        protected float m_scale_step = 2;
        protected List<Model.ItemMove> m_list_child_born = new List<Model.ItemMove>();
        protected bool is_breeding = false;
        protected int m_compt_child = 1;

        protected Entity ent;
        protected bool m_display_pattern = true;

        public Entity3D(Map3D par_copy_world, Model.ItemMove par_item, Root par_root, SceneManager par_sc, string par_name, int par_l)
        {
            m_item = par_item;
            m_world = par_copy_world;
            m_copy_world = par_copy_world.Elements3D;
            m_root = par_root;
            m_scnmgr = par_sc;
            m_name = par_name;
            m_length_item = par_l;
            m_pattern = new Pattern3D(m_root, m_scnmgr, m_name + "-pattern3D");
            
        }

      
        public bool Skeletal_FrameStarted(FrameEvent evt)
        {

            m_delta = evt.timeSinceLastFrame ;

            if (m_item == null || m_item.Killed)
            {
                // m_item = new Model.ItemMove();
                m_position_3D.Occupants3D.Remove(this);

                DestroyEntity();
                return true;
            }

            
            if (!m_walking)
            {
                if (m_item.Direction == "_none")
                {
                    m_angle = 90;
                }
                else
                {
                    m_angle = m_angle_orientation;
                }

                // get the behavior of our model
                m_item.Live();
              
                // check new born from the Model
                UpdateChildren();

                // update all graphics elements needed to view our interactions
                UpdateIHM();

                //  update position in the field thanks to the knowledge of the map
                try
                {
                    m_position_3D.Occupants3D.Remove(this);
                }
                catch (Exception e)
                {

                }

                m_position_3D = SearchElement(m_item.PositionNow);
                m_position_3D.Occupants3D.Add(this);

                // update vectors 3
                m_tmp_v3.x = m_item.PositionNow.I * m_length_item;
                m_tmp_v3.y = 5 * m_item.PositionNow.Height + 11.5f + m_dec;
                m_tmp_v3.z = m_item.PositionNow.J * m_length_item;

                m_destination = m_tmp_v3;
                m_direction = m_destination - m_node.Position;
                m_distance = m_direction.Normalise();

                switch (m_item.Direction)
                {
                    case "_north":      m_angle_next = m_angle;       m_angle_orientation = 0;   break;
                    case "_north_east": m_angle_next = m_angle - 45;  m_angle_orientation = 45;  break;
                    case "_east":       m_angle_next = m_angle - 90;  m_angle_orientation = 90;  break;
                    case "_south_east": m_angle_next = m_angle - 135; m_angle_orientation = 135; break;

                    case "_south":      m_angle_next = m_angle - 180; m_angle_orientation = 180; break;
                    case "_south_west": m_angle_next = m_angle - 225; m_angle_orientation = 225; break;
                    case "_west":       m_angle_next = m_angle - 270; m_angle_orientation = 270; break;
                    case "_north_west": m_angle_next = m_angle - 315; m_angle_orientation = 315; break;
                    default: break;
                }

                m_angle_radian = System.Math.PI * (m_angle_next) / 180;
                m_node.Yaw((float)m_angle_radian + m_decalage);
                m_walking = true;

                }
                else
                {
                    m_distance -= m_walkSpeed * m_delta;
                    // check to see if we've arrived at a waypoint
                    if (m_distance <= 0.0f)
                    {
                          
                        // set our node to the destination we've just reached & reset direction to 0
                        m_node.Position = m_destination;
                        m_direction = Vector3.ZERO;
                        m_walking = false;

                    }
                           
                    else
                    {
                        m_speed = m_walkSpeed * m_delta;
                        // movement code goes here
                        m_node.Translate(m_direction * m_walkSpeed * m_delta);

                    }
                }


            if (_animState_bot != null)
            {
                _animState_bot.AddTime(m_delta * m_decalage_speed);
            }

            _animState.AddTime(m_delta * m_decalage_speed);
      
            return true;
        }

        public void UpdateIHM()
        {
            if (m_display_pattern)
            {
                // display elements viewed/perceived by entity
                ChangeColor();
            }

            // display the decision chosen by itemMove
            DisplayDecision();

            // display a breeding between preys or between predators
            DisplayBreeding();

            UpdateGrowth();
        }

        public virtual void UpdateChildren()
        {

        }

        public virtual void UpdateGrowth()
        {

        }

        public void DisplayBreeding()
        {
            if (is_breeding)
            {
                m_breed = new Breed3D(m_root, m_scnmgr, m_name + "_breed", m_node);
                m_breed.Initialisation();
            }

            is_breeding = false;
        }

        public void DisplayKilled()
        {

            m_die = new Die3D(m_root, m_scnmgr, m_name + "_die", m_node.Position);
            m_die.Initialisation();
        }


        public void DetectionEntity()
        {
            /*
            if (m_rays.Count > 0)
            {
                foreach (RayLine loc_ray in m_rays)
                {
                    loc_ray.Destroy();
                }
            }k

            m_rays = new List<RayLine>();
        

      
            
            
            foreach (Model.Element loc_elem in m_item.ElementsVisible)
            {
                Element3D loc_3D = SearchElement(loc_elem);
                foreach (Entity3D loc_entity in loc_3D.Occupants3D)
                {
                    RayLine loc_ray = new RayLine(m_root, m_scnmgr, "_ray" + m_name + m_cpt, this, loc_entity);
                    loc_ray.Initialisation();
                    m_cpt++;
                    m_rays.Add(loc_ray);
                }
            }
             */
            
            /*
            foreach (Model.ItemMove loc_mov in m_item.ItemsViewed)
            {
                Element3D loc_3D = SearchElement(loc_mov.PositionNow);
                RayLine loc_ray = new RayLine(m_root, m_scnmgr, "_ray" + m_name + m_cpt, this, loc_3D);
                loc_ray.Initialisation();
                m_cpt++;
                m_rays.Add(loc_ray);
            }*/
            
            //m_rays.Add(manOb);
        }


        public void DisplayDecision()
        {

            string tmp = m_item.TypeDecision;

            switch (tmp)
            {
                case "_hunting": 
                     m_billboard_set.SetMaterialName("Examples/HuntingDecision"); 
                    break;

                case "_eating":
                    m_billboard_set.SetMaterialName("Examples/EatingDecision");
                    break;

                case "_breeding":
                    m_billboard_set.SetMaterialName("Examples/BreedingDecision");
                    break;

                case "_escaping":
                    m_billboard_set.SetMaterialName("Examples/EscapeDecision");
                    break;

                default:
                    m_billboard_set.SetMaterialName("Examples/NoDecision");
                    break;

            }

        }

        public void ChangeColor()
        {
            m_pattern.Destroy();
            m_elements_3D_viewed = new List<Element3D>();
            m_elements_a_star = new List<Element3D>();
            m_elements_perception = new List<Element3D>();

            // creation of a new pattern object to avoid SceneNode conflicts
            m_pattern.UpdateConflict();

            foreach (Model.Element loc_elem in m_item.ElementsVisible)
            {
                Element3D loc_3D = SearchElement(loc_elem);
                m_elements_3D_viewed.Add(loc_3D);
            }

            foreach (Model.Element loc_elem in m_item.ElementsPath)
            {
                Element3D loc_3D = SearchElement(loc_elem);
                m_elements_a_star.Add(loc_3D);
            }

            foreach (Model.Element loc_elem in m_item.Perception)
            {
                Element3D loc_3D = SearchElement(loc_elem);
                m_elements_perception.Add(loc_3D);
            }

            m_pattern.Element3D = m_elements_3D_viewed;
            m_pattern.ElementsAStar = m_elements_a_star;
            m_pattern.ElementsPerception = m_elements_perception;
            m_pattern.Initialisation();
           
        }

        private bool IsField3D(object o)
        {
            if (o is Field3D)
                return true;

            return false;
        }

        private Element3D SearchElement(Model.Element par_item)
        {
            foreach (Element3D item in m_copy_world)
                 if (item.ModelElement == par_item)
                     return item;
            return null;
        }

        public void DestroyEntity()
        {
            m_root.FrameStarted -= new FrameListener.FrameStartedHandler(Skeletal_FrameStarted);

            DisplayKilled();


           // m_scnmgr.DestroyEntity(m_name);
           // m_scnmgr.DestroySceneNode(m_name);

            m_pattern.Destroy();
            m_node.DetachAllObjects();
            m_scnmgr.DestroyEntity(m_name);

           // m_world.Killentity(this);

        }

        public SceneNode Node
        {
            get { return m_node; }
        }

        public Element3D PositionElement
        {
            get { return m_position_3D; }
            set { m_position_3D = value; }
        }

        public float Speed
        {
            get { return m_speed; }
            set { m_speed = value; }
        }

        

        public Vector3 Direction
        {
            get { return m_direction; }
            set { m_direction = value; }
        }
        
         public bool PatternDisplay
        {
            get { return m_display_pattern; }
            set { m_display_pattern = value; }
        }


         public Pattern3D Pattern
         {
             get { return m_pattern; }
             set { m_pattern = value; }
         }
    }
}



